<body></body>

<script>
  // 1、组件本质是一个函数返回虚拟 dom
  const MyComponent = function () {
    return {
      type: 'div',
      props: {
        onClick: () => alert('hello'),
      },
      children: 'click me',
    }
  }
  // 2、renderer渲染对组件和标签元素作区分
  const renderer = (vnode, container) => {
    if (typeof vnode.type == 'string') {
      mountElement(vnode, container)
    } else if (typeof vnode.type == 'function') {
      mountComponet(vnode, container)
    } else if (typeof vnode.type == 'object') {
      mountComponet_Obj(vnode, container)
    }
  }
  //---- 标签元素
  function mountElement(vnode, container) {
    // 1、创建元素
    const el = document.createElement(vnode.type)
    // 2、绑定事件
    for (const key in vnode.props) {
      if (/^on/.test(key)) {
        el.addEventListener(
          key.substr(2).toLowerCase(), //事件名称
          vnode.props[key] //事件处理函数
        )
      }
    }
    // 3、处理children
    if (typeof vnode.children === 'string') {
      el.appendChild(document.createTextNode(vnode.children))
    } else if (typeof vnode.children) {
      vnode.children.forEach((child) => renderer(child, el))
    }
    container.appendChild(el)
  }
  //----- 组件： 一个函数返回虚拟 dom
  function mountComponet(vnode, container) {
    // 调用组件函数，获取组件要渲染的内容（虚拟 DOM）
    const subtree = vnode.type()
    // 组件里面也可能是组件，所以递归调用renderer
    renderer(subtree, container)
  }
  //----- 组件： 对象形式，调用render返回 dom
  function mountComponet_Obj(vnode, container) {
    const subtree = vnode.render()
    renderer(subtree, container)
  }
  // const vnode = { type: MyComponent }
  // renderer(vnode, document.body)

  const MyComponentOBJ = {
    render() {
      return {
        type: 'div',
        props: {
          onClick: () => alert('hello'),
        },
        children: 'click me',
      }
    },
  }
  renderer({ type: MyComponentOBJ }, document.body)
</script>
